﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.IO;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using VideoLink.Frames;
using VideoLink.Utility;

namespace VideoLink.Streams
{
    /// <summary>
    /// Представляет панель, на которой размещаются приёмники и источники видео.
    /// </summary>
    public partial class VideosPanel : UserControl
    {
        #region Fields

        /// <summary>
        /// Указатель на функцию, выполняющая действия по созданию нового источника видео.
        /// Данный указатель изменяется при запросе пользователя о создании потока некоторого типа.
        /// </summary>
        private Func<string, VideoSourceBase> newVideoSource;

        /// <summary>
        /// Тип создаваемого потока
        /// </summary>
        private StreamType? newStreamType;

        /// <summary>
        /// Содержит информацию о потоках, которые были созданы, 
        /// но информация о них не была отправлена.
        /// </summary>
        private List<VideoStreamInfo> newlyAdded = new List<VideoStreamInfo>();

        /// <summary>
        /// Содержит ID потоков, которые были закрыты на данной стороне,
        /// но ещё не были закрыты на той стороне.
        /// </summary>
        private List<int> deleted = new List<int>();
        
        #endregion

        /// <summary>
        /// Создать новый экземпляр класса по умолчанию
        /// </summary>
        public VideosPanel()
        {
            InitializeComponent();
        }

        /// <summary>
        /// Закрыть все потоки.
        /// </summary>
        public void CloseStreams()
        {
            foreach (var stream in videosLP.Controls)
                if (stream is VideoSourceBase)
                    (stream as VideoSourceBase).Close();
                else if (stream is VideoReceiverBase)
                    (stream as VideoReceiverBase).Close();
        }

        #region Video management

        /// <summary>
        /// Добавить новый исходящий поток, воспроизводящий видео файл
        /// и посылающий его покадрово.
        /// </summary>
        public void AddNewByFrame()
        {
            newVideoSource = (fileName) =>
                new ByFrameDSSource(GetID(), fileName);
            newStreamType = StreamType.ByFrame;
            openVideoDialog.ShowDialog();
        }

        /// <summary>
        /// Добавить новый исходящий поток, воспроизводящий последовательность
        /// графических файлов и посылающий видео покадрово.
        /// </summary>
        public void AddNewJPEGStream()
        {
            var path = TemplateRequestDialog.RequestTemplate();
            if (String.IsNullOrEmpty(path))
                return;

            newStreamType = StreamType.ByFrame;
            AddOutgoing(new ByFrameJPEGSource(GetID(), path, 25));
        }

        /// <summary>
        /// Добавить новый исходящий поток, воспроизводящий видео файл
        /// и посылающий его целиком.
        /// </summary>
        public void AddNewEntire()
        {
            newVideoSource = (fileName) =>
                new EntireDSSource(GetID(), fileName);

            newStreamType = StreamType.EntireVideo;
            openVideoDialog.ShowDialog();
        }
        
        /// <summary>
        /// Обработчик события выбора пути до видео файла.
        /// </summary>
        /// <param name="sender">-</param>
        /// <param name="e">-</param>
        private void openVideoDialog_FileOk(object sender, CancelEventArgs e)
        {
            AddOutgoing(newVideoSource(openVideoDialog.FileName));
        }

        /// <summary>
        /// Добавить указанный исходящий поток.
        /// </summary>
        /// <param name="newStream">Источник видео</param>
        private void AddOutgoing(VideoSourceBase newStream)
        {
            newlyAdded.Add(new VideoStreamInfo(newStream.StreamID,
                newStreamType.Value));
            newStreamType = null;
            videosLP.Controls.Add(newStream);
            newStream.NeedClose += new EventHandler(newStream_NeedClose);
        }

        /// <summary>
        /// Добавить приёмник видео, обрабатывающий указанный поток.
        /// </summary>
        /// <param name="newStream">Информация о принимаемом потоке.</param>
        private void AddIncoming(VideoStreamInfo newStream)
        {
            VideoReceiverBase control = null;
            switch (newStream.Type)
            { 
                case StreamType.ByFrame:
                    control = new ByFrameJPEGReceiver(newStream);
                    break;
                case StreamType.EntireVideo:
                    control = new EntireDSReceiver(newStream);
                    break;
            }

            videosLP.Controls.Add(control);
            control.NeedClose += new EventHandler(newStream_NeedClose);
        }

        /// <summary>
        /// Закрыть указанный поток.
        /// </summary>
        /// <param name="stream">Приёмник или обработчик</param>
        private void CloseStream(Control stream)
        {
            videosLP.Controls.Remove(stream);
            if (stream is VideoReceiverBase)
            {
                var receiver = (stream as VideoReceiverBase);
                receiver.NeedClose -= new EventHandler(newStream_NeedClose);
                receiver.Close();
                deleted.Add(receiver.StreamID);
            }
            else if (stream is VideoSourceBase)
            {
                var source = (stream as VideoSourceBase);
                source.NeedClose -= new EventHandler(newStream_NeedClose);
                source.Close();
                deleted.Add(source.StreamID);
            }
        }

        /// <summary>
        /// Вызывается, когда пользователь закрывает источник или приёмник видео.
        /// </summary>
        /// <param name="sender">-</param>
        /// <param name="e">-</param>
        private void newStream_NeedClose(object sender, EventArgs e)
        {
            CloseStream(sender as Control);
        }

        /// <summary>
        /// Получить новый ID потока.
        /// </summary>
        /// <returns>Уникальный ID потока</returns>
        private int GetID()
        {
            var nextID = 0;
            foreach(var stream in videosLP.Controls)
                if (stream is VideoSourceBase)
                    nextID = Math.Max(nextID,
                        (stream as VideoSourceBase).StreamID);
                else if (stream is VideoReceiverBase)
                    nextID = Math.Max(nextID,
                        (stream as VideoReceiverBase).StreamID);
            return nextID + 1;
        }

        #region Unnecessary methods now

        /*
        private void addTVGByFrameMenuItem_Click(object sender, EventArgs e)
        {
            newVideoSource = (fileName) =>
                    new ByFrameTVGSource(GetID(), fileName);
            openVideoDialog.ShowDialog();
        }
        */

        #endregion

        #endregion

        #region Data exchange

        /// <summary>
        /// Получить информацию о текущем состоянии всех исходящих потоков видео.
        /// </summary>
        /// <returns>
        /// Информация о текущем состоянии всех исходящих потоков видео.
        /// </returns>
        public Frame GetFrame()
        {
            var running = new List<VideoStreamState>();

            foreach (var stream in videosLP.Controls.OfType<VideoSourceBase>())
            {
                var data = stream.GetFrameData();
                if (data != null)
                    running.Add(data);
            }

            var result = new Frame(new List<VideoStreamInfo>(newlyAdded),
                running,
                new List<int>(deleted));

            newlyAdded.Clear();
            deleted.Clear();

            return result;
        }

        /// <summary>
        /// Обработать новый, только что полученный, пакет с информацией о входящих потоках.
        /// </summary>
        /// <param name="frame">
        /// Информация о входящих потоках.
        /// </param>
        public void NewFrameReceived(Frame frame)
        {
            var oldIncoming = videosLP.Controls.OfType<VideoReceiverBase>();

            foreach (var deletedID in frame.JustDeleted)
            { 
                var toClose = oldIncoming
                    .FirstOrDefault((stream) => stream.StreamID == deletedID);
                if (toClose != null)
                    CloseStream(toClose);
            }
            
            foreach (var newStream in frame.NewlyAdded)
                AddIncoming(newStream);

            foreach (var running in frame.Running)
            {
                var toUpdate = oldIncoming
                    .FirstOrDefault((stream) => stream.StreamID == running.StreamID);
                if (toUpdate != null)
                    toUpdate.NewFrameReceived(running);
            }
        }

        #endregion
    }
}